package com.liuugoqing.structural.proxy.jdk_proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理类工厂 -- 用于获取动态代理所生成的类
 *
 * @Author : liugq
 * @Date : 2022/7/3 23:45
 * @See : 参考类
 * @Since： JDK1.8
 * @Version : 1.0
 */
public class ProxyFactory {

    /**
     * 获取代理对象
     * @param obj 被代理接口的子实现
     * @return 代理成功的类
     */
    public static SellTickets getProxyInstance(SellTickets obj) { //obj:被代理类对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);


        /**
         * 动态代理：
         *  特点：字节码随用随创建，随用随加载
         *  作用：不修改源码的基础上对方法增强
         *  分类：
         *      基于接口的动态代理
         *      基于子类的动态代理
         *  基于接口的动态代理：
         *      涉及的类：Proxy
         *      提供者：JDK官方
         *  如何创建代理对象：
         *      使用Proxy类中的newProxyInstance方法
         *  创建代理对象的要求：
         *      被代理类最少实现一个接口，如果没有则不能使用
         *  newProxyInstance方法的参数：
         *      ClassLoader：类加载器
         *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
         *      Class[]：字节码数组
         *          它是用于让代理对象和被代理对象有相同方法。固定写法。
         *      InvocationHandler：用于提供增强的代码
         *          它是让我们写如何代理。我们一般都是些一个该接口的实现类，通常情况下都是匿名内部类，但不是必须的。
         *          此接口的实现类都是谁用谁写。
         */
        return (SellTickets)Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),   //加载被代理类
                obj.getClass().getInterfaces(),    //获取被代理类中实现的接口
                handler
        );
    }


    /**
     * 实现InvocationHandler接口
     */
    static class MyInvocationHandler implements InvocationHandler {
        private Object obj; //需要使用被代理类的对象经行赋值

        void bind(Object obj) {
            this.obj = obj;
        }
        /**
         * 作用：执行被代理对象的任何接口方法都会经过该方法
         * @param proxy   代理对象的引用
         * @param method  当前执行的方法
         * @param args    当前执行方法所需的参数
         * @return        和被代理对象方法有相同的返回值
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("收取服务费");
            //返回方法的返回值
            return method.invoke(obj,args);
        }
    }


}
